#include "gpiocontroller.h"
#include "configmanager.h"
#include "ImageProcessor.h"
#include <QThread>
#include <QLoggingCategory>

Q_LOGGING_CATEGORY(gpioController, "gpio.controller")

GpioController &GpioController::getInstance()
{
    static GpioController _instance;
    return _instance;
}

GpioController::GpioController(QObject *parent) : QObject(parent)
    ,setting(QString("master/config/")  + "gpio.ini", QSettings::IniFormat)
{
    m_gpioversion.init(&setting);


    if(setupGpio())
    {
        qWarning() << "fail to setupGPIO";
        return;
    }
    //monitorInputs();
    isRunning = true;
    monitorGPIO4();
    monitorGPIO2();
    pilotLampYellow();

}

GpioController::~GpioController()
{
    isRunning = false;
    LogDebug << "quit GpioController;";
    for (auto &chipPair : chips) {
        gpiod_chip_close(chipPair.second);
    }
}

void GpioController::pilotLampYellow()
{
    pilotLampStatus_ = LAMP_YELLOW;
    setGpioDO4(false);
    setGpioDO5(true);
    setGpioDO6(false);
}

void GpioController::pilotLampGreen()
{
    pilotLampStatus_ = LAMP_GREEN;
    setGpioDO4(false);
    setGpioDO5(false);
    setGpioDO6(true);
}

void GpioController::pilotLampRed()
{
    pilotLampStatus_ = LAMP_RED;
    setGpioDO4(true);
    setGpioDO5(false);
    setGpioDO6(false);
}

void GpioController::pilotLampBuzzer()
{
    GpioController::getInstance().setGpioDO7(true);
    QTimer::singleShot(1000, []() {
        // 50ms后拉低 GpioDO1
        GpioController::getInstance().setGpioDO7(false);
    });
}

void GpioController::plcStart()
{
    setGpioDO1(true);
    QTimer::singleShot(50, [this](){
        setGpioDO1(false);
    });
}


void GpioController::plcStop()
{
    qInfo() << "GpioController::plcStop";
    setGpioDO2(true);
    QTimer::singleShot(50, [this](){
        setGpioDO2(false);
    });
}

int GpioController::setupGpio()
{
    LogDebug << "Attempting to setupGpio";

    QStringList doKeys;
    QStringList diKeys;
    if(gpioversion() == 1)
    {
        doKeys = QStringList{"gpioDO1", "gpioDO2", "gpioDO3", "gpioDO4",
                             "gpioDO5", "gpioDO6", "gpioDO7", "gpioDO8","gpioDO9"};
        diKeys = QStringList{"gpioDI1", "gpioDI2", "gpioDI3", "gpioDI4" , "gpioDI5"};
    }else if(gpioversion() == 2)
    {
        doKeys = QStringList{"v2gpioDO1", "v2gpioDO2", "v2gpioDO3", "v2gpioDO4",
                             "v2gpioDO5", "v2gpioDO6", "v2gpioDO7", "v2gpioDO8","v2gpioDO9"};

        diKeys = QStringList{"v2gpioDI1", "v2gpioDI2", "v2gpioDI3", "v2gpioDI4" , "v2gpioDI5"};
    }else
        return -1;

    for (int i = 0; i < doKeys.size(); ++i) {
        QString pinName = ParamValue.getParameter<QString>("gpioModule", doKeys[i]);
        GpioLine gpioLine = getGpioLine(pinName);
        if (!gpioLine.chip || !gpioLine.line) {
            qCWarning(gpioController) << "Invalid GPIO pin:" << pinName;
            continue;
        }
        doLines[i] = gpioLine;
        gpiod_line_request_output(gpioLine.line, "gpio_controller", 0);
        qCInfo(gpioController) << "Configured" << doKeys[i] << "to pin" << pinName;
    }


    for (int i = 0; i < diKeys.size(); ++i) {
        QString pinName = ParamValue.getParameter<QString>("gpioModule", diKeys[i]);
        qInfo() << "GOIO In pinName:" << pinName;
        GpioLine gpioLine = getGpioLine(pinName);

        if (!gpioLine.chip || !gpioLine.line) {
            qCWarning(gpioController) << "Invalid GPIO pin:" << pinName;
            continue;
        }
        diLines[i] = gpioLine;
            //gpiod_line_request_input(gpioLine.line, "gpio_controller");

        //gpiod_line_request_falling_edge_events
        if(i==2 )
        {
            int ret = gpiod_line_request_falling_edge_events(diLines[i].line, "gpio-monitor");
            if (ret < 0) {
                perror("Error");
                LogError << "Request events failed";
                gpiod_chip_close(diLines[i].chip);
                return -1;
            }
        }else
        {
            int ret = gpiod_line_request_rising_edge_events(diLines[i].line, "gpio-monitor");
            if (ret < 0) {
                perror("Error");
                LogError << "Request events failed";
                gpiod_chip_close(diLines[i].chip);
                return -1;
            }
        }
        qCInfo(gpioController) << "Configured IO" << diKeys[i] << "to pin" << pinName;

    }
    return 0;
}

GpioController::GpioLine GpioController::getGpioLine(const QString &gpioString)
{
    int chipNum = parseGpioChip(gpioString);
    int lineNum = parseGpioLine(gpioString);

    LogDebug << QString("getGpioLine : %1,%2")
                    .arg(chipNum).arg(lineNum);
    if (chipNum < 0 || lineNum < 0) {
        return {nullptr, nullptr};
    }

    if (chips.find(chipNum) == chips.end()) {
        QString chipPath = QString("/dev/gpiochip%1").arg(chipNum);
        chips[chipNum] = gpiod_chip_open(chipPath.toStdString().c_str());
        if (!chips[chipNum]) {
            qCWarning(gpioController) << "Failed to open" << chipPath;
            return {nullptr, nullptr};
        }
    }

    struct gpiod_line *line = gpiod_chip_get_line(chips[chipNum], lineNum);
    if (!line) {
        qCWarning(gpioController) << "Failed to get line" << lineNum << "from chip" << chipNum;
        return {nullptr, nullptr};
    }

    return {chips[chipNum], line};
}

int GpioController::parseGpioLine(const QString &gpioString)
{
    // Example: "GPIO6_A6"
    QRegExp rx("^GPIO(\\d+)_A(\\d+)$");
    if (rx.indexIn(gpioString) != -1) {
        int pin = rx.cap(2).toInt();
        return pin;
    }
    return -1;
}

int GpioController::parseGpioChip(const QString &gpioString)
{
    // Example: "GPIO6_A6"
    QRegExp rx("^GPIO(\\d+)_A(\\d+)$");
    if (rx.indexIn(gpioString) != -1) {
        int chip = rx.cap(1).toInt();
        return chip;
    }
    return -1;
}

void GpioController::monitorGPIO4()
{
    QThread *thread = QThread::create([this]() {
        pthread_t threadId = pthread_self();

        // 设置实时调度策略和优先级
        struct sched_param param;
        param.sched_priority = 99;
        if (pthread_setschedparam(threadId, SCHED_FIFO, &param) != 0) {
            qWarning() << "Failed to set monitorGPIO4 real-time scheduling policy";
        } else {
            qWarning() << "Successfully set monitorGPIO4 real-time scheduling policy";
        }

        struct gpiod_line_bulk bulk_gpio4, event_bulk_gpio4;
        gpiod_line_bulk_init(&bulk_gpio4);
        for (int i = 0; i < 4; ++i) {
            gpiod_line_bulk_add(&bulk_gpio4, diLines[i].line);
        }

        while (isRunning) {
            gpiod_line_bulk_init(&event_bulk_gpio4);
            int ret = gpiod_line_event_wait_bulk(&bulk_gpio4, nullptr, &event_bulk_gpio4);
            if (ret < 0) {
                perror("gpiod_line_event_wait_bulk GPIO4");
                LogError << "Wait event failed for GPIO4";
                break;
            }
            processEvents(event_bulk_gpio4, 0, 4); // DI1-DI4
        }
    });

    thread->start();
    thread->setPriority(QThread::TimeCriticalPriority);
}

void GpioController::monitorGPIO2()
{
    QThread *thread = QThread::create([this]() {
        pthread_t threadId = pthread_self();

        // 设置实时调度策略和优先级
        struct sched_param param;
        param.sched_priority = 90;
        if (pthread_setschedparam(threadId, SCHED_FIFO, &param) != 0) {
            qWarning() << "Failed to set monitorGPIO2 real-time scheduling policy";
        } else {
            qWarning() << "Successfully set monitorGPIO2 real-time scheduling policy";
        }

        struct gpiod_line_bulk bulk_gpio2, event_bulk_gpio2;
        gpiod_line_bulk_init(&bulk_gpio2);
        gpiod_line_bulk_add(&bulk_gpio2, diLines[4].line); // DI5

        while (isRunning) {
            gpiod_line_bulk_init(&event_bulk_gpio2);
            int ret = gpiod_line_event_wait_bulk(&bulk_gpio2, nullptr, &event_bulk_gpio2);
            if (ret < 0) {
                perror("gpiod_line_event_wait_bulk GPIO2");
                LogError << "Wait event failed for GPIO2";
                break;
            }
            processEvents(event_bulk_gpio2, 4, 5); // DI5
        }
    });

    thread->start();
    thread->setPriority(QThread::TimeCriticalPriority);
}

void GpioController::processEvents(struct gpiod_line_bulk &event_bulk, int startIndex, int endIndex)
{
    int getEventNum = gpiod_line_bulk_num_lines(&event_bulk);
    //qDebug() << "getEventNum" << getEventNum;
    for (unsigned int j = 0; j < getEventNum; ++j) {
        struct gpiod_line *line = event_bulk.lines[j];

        struct gpiod_line_event event;
        int ret = gpiod_line_event_read(line, &event);
        if (ret < 0) {
            LogError << "Read event failed for line";
            continue;
        }

        int line_index = -1;
        for (int i = startIndex; i < endIndex; ++i) {
            if (diLines[i].line == line) {
                line_index = i;
                break;
            }
        }
        if (line_index == -1) continue;

        if (event.event_type == GPIOD_LINE_EVENT_RISING_EDGE || event.event_type == GPIOD_LINE_EVENT_FALLING_EDGE) {
            qCInfo(gpioController) << "Input" << line_index + 1 << "changed";
            handleEvent(line_index);
        }
    }
}

void GpioController::handleEvent(int line_index)
{
    switch (line_index) {
    case 0: emit infrared1trigger(); break;
    case 1: emit infrared2trigger(); break;
    case 2: emit shortageGrigger(); break;
    case 3: emit inplaceGrigger(); break;
    case 4: emit replenishmentGrigger(); break;
    }
    if (ImageProcessor::getInstance().selectedStep() == ImageProcessor::MeauIO) {
        switch (line_index) {
        case 0: emit gpioDI1Changed(); break;
        case 1: emit gpioDI2Changed(); break;
        case 2: emit gpioDI3Changed(); break;
        case 3: emit gpioDI4Changed(); break;
        case 4: emit gpioDI5Changed(); break;
        }
    }
}


bool GpioController::gpioDO1() const { return gpiod_line_get_value(doLines[0].line); }
void GpioController::setGpioDO1(bool value) {
    gpiod_line_set_value(doLines[0].line, value);
    qCInfo(gpioController) << "Set DO1 to" << value;
    emit gpioDO1Changed();
}

bool GpioController::gpioDO2() const { return gpiod_line_get_value(doLines[1].line); }
void GpioController::setGpioDO2(bool value) {
    gpiod_line_set_value(doLines[1].line, value);
    qCInfo(gpioController) << "Set DO2 to" << value;
    emit gpioDO2Changed();
}

bool GpioController::gpioDO3() const { return gpiod_line_get_value(doLines[2].line); }
void GpioController::setGpioDO3(bool value) {
    gpiod_line_set_value(doLines[2].line, value);
    qCInfo(gpioController) << "Set DO3 to" << value;
    emit gpioDO3Changed();
}

bool GpioController::gpioDO4() const { return gpiod_line_get_value(doLines[3].line); }
void GpioController::setGpioDO4(bool value) {
    gpiod_line_set_value(doLines[3].line, value);
    qCInfo(gpioController) << "Set DO4 to" << value;
    emit gpioDO4Changed();
}

bool GpioController::gpioDO5() const { return gpiod_line_get_value(doLines[4].line); }
void GpioController::setGpioDO5(bool value) {
    gpiod_line_set_value(doLines[4].line, value);
    qCInfo(gpioController) << "Set DO5 to" << value;
    emit gpioDO5Changed();
}

bool GpioController::gpioDO6() const { return gpiod_line_get_value(doLines[5].line); }
void GpioController::setGpioDO6(bool value) {
    gpiod_line_set_value(doLines[5].line, value);
    qCInfo(gpioController) << "Set DO6 to" << value;
    emit gpioDO6Changed();
}

bool GpioController::gpioDO7() const { return gpiod_line_get_value(doLines[6].line); }
void GpioController::setGpioDO7(bool value) {
    gpiod_line_set_value(doLines[6].line, value);
    qCInfo(gpioController) << "Set DO7 to" << value;
    emit gpioDO7Changed();
}

bool GpioController::gpioDO8() const { return gpiod_line_get_value(doLines[7].line); }
void GpioController::setGpioDO8(bool value) {
    gpiod_line_set_value(doLines[7].line, value);
    qCInfo(gpioController) << "Set DO8 to" << value;
    emit gpioDO8Changed();
}

bool GpioController::gpioDO9() const { return gpiod_line_get_value(doLines[7].line); }
void GpioController::setGpioDO9(bool value) {
    gpiod_line_set_value(doLines[8].line, value);
    qCInfo(gpioController) << "Set DO8 to" << value;
    emit gpioDO8Changed();
}

bool GpioController::gpioDI1() const {
    if (diLines[0].line == nullptr) {
        //LogError << "GPIO line 0 is not initialized.";
        return false;  // 或者其他适当的默认值
    }
    return static_cast<bool>(gpiod_line_get_value(diLines[0].line));
}

bool GpioController::gpioDI2() const {
    if (diLines[1].line == nullptr) {
        //LogError << "GPIO line 1 is not initialized.";
        return false;
    }
    return static_cast<bool>(gpiod_line_get_value(diLines[1].line));
}

bool GpioController::gpioDI3() const {
    if (diLines[2].line == nullptr) {
        //LogError << "GPIO line 2 is not initialized.";
        return false;
    }
    return static_cast<bool>(gpiod_line_get_value(diLines[2].line));
}

bool GpioController::gpioDI4() const {
    if (diLines[3].line == nullptr) {
        //LogError << "GPIO line 3 is not initialized.";
        return false;
    }
    return static_cast<bool>(gpiod_line_get_value(diLines[3].line));
}

bool GpioController::gpioDI5() const {
    if (diLines[4].line == nullptr) {
        //LogError << "GPIO line 4 is not initialized.";
        return false;
    }
    return static_cast<bool>(gpiod_line_get_value(diLines[4].line));
}

